let activeEffect = null;
const effectStack = []; //effect栈
const bucket = new WeakMap();
//确保ITERATE_KEY是唯一，设置对象的迭代
const ITERATE_KEY = Symbol();

function track(target, key) {
          // 当禁止追踪时，直接返回
          if (!activeEffect || !shouldTrak) return;
          let depMap = bucket.get(target);
          if (!depMap) bucket.set(target, (depMap = new Map()));

          let deps = depMap.get(key);
          if (!deps) depMap.set(key, (deps = new Set()));

          const effectSet = deps.add(activeEffect);
          activeEffect.deps.push(effectSet);
}

// 增加第四个参数newVal
function trigger(target, key, type, newVal) {
          let depMap = bucket.get(target);
          if (!depMap) return;
          const effects = depMap.get(key);
          const effectsToRun = new Set();
          // ADD 或 DELETE 操作时，需要触发与ITERATE_KEY 相关联的副作用函数重新执行
          if (type === "ADD" || type == "DELETE") {
                    const iterateEffects = depMap.get(ITERATE_KEY);
                    iterateEffects &&
                              iterateEffects.forEach((effectFn) => {
                                        if (effectFn !== activeEffect) {
                                                  effectsToRun.add(effectFn);
                                        }
                              });
          }

          effects &&
                    effects.forEach((effectFn) => {
                              if (effectFn !== activeEffect) {
                                        effectsToRun.add(effectFn);
                              }
                    });
          // 操作类型是数组的ADD，执行 length相关副作用
          if (type === "ADD" && Array.isArray(target)) {
                    console.log("add");

                    const lengthEffect = depMap.get("length");
                    lengthEffect &&
                              lengthEffect.forEach((effectFn) => {
                                        if (effectFn !== activeEffect) {
                                                  effectsToRun.add(effectFn);
                                        }
                              });
          }

          // 如果数组修改了 length 属性
          if (Array.isArray(target) && key === "length") {
                    // 将索引大于 or 等于新 length  的元素相关副作用函数添加到effectsToRun
                    depMap.forEach((effects, key) => {
                              if (typeof key === "number" && newVal <= key) {
                                        //这里遍历的key是数组的索引，0，1，2，3
                                        effects.forEach((effectFn) => {
                                                  if (
                                                            effectFn !==
                                                            activeEffect
                                                  ) {
                                                            effectsToRun.add(
                                                                      effectFn
                                                            );
                                                  }
                                        });
                              }
                    });
          }
          //
          effectsToRun.forEach((effectFn) => {
                    if (effectFn?.options?.scheduler) {
                              effectFn?.options?.scheduler(effectFn);
                    } else {
                              effectFn();
                    }
          });
}

function effect(fn, options) {
          // 这里的 effectFn是一个独立函数，就是每一次effect开辟一个新的空间，使用的是新的指针
          function effectFn() {
                    cleanup(effectFn);
                    activeEffect = effectFn;

                    effectStack.push(effectFn);
                    const res = fn();
                    effectStack.pop(effectFn);
                    activeEffect = effectStack[effectStack.length - 1];
                    return res;
          }
          effectFn.options = options;
          effectFn.deps = []; //反向收集 effectSet的引用
          if (!effectFn?.options?.lazy) effectFn();
          return effectFn;
}
function cleanup(effectFn) {
          effectFn.deps.forEach((depSet) => depSet.delete(effectFn));
          effectFn.deps.length = 0; //遍历所有的 Set集合删除同一个副作用
}

const jobQueue = new Set(); // 任务队列（自动去重）
let isFlushing = false; // 队列刷新状态
// 任务队列控制逻辑
function flushJob() {
          if (isFlushing) return;
          isFlushing = true; // 微任务触发器
          Promise.resolve()
                    .then(() => jobQueue.forEach((job) => job()))
                    .finally(() => (isFlushing = false));
}

// computed:把 getter 作为副作用函数，创建一个 lazy 的 effect
function computed(getter) {
          let value; // 缓存值
          let dirty = true; // 标记是否需要重新计算

          const effectFn = effect(getter, {
                    lazy: true, // 延迟执行
                    scheduler() {
                              dirty = true; // 依赖变化时标记为需要重新计算
                              trigger(obj, "value"); // 触发依赖更新
                    },
          });

          const obj = {
                    get value() {
                              if (dirty) {
                                        value = effectFn(); // 执行计算
                                        dirty = false; // 标记为已计算
                              }
                              track(obj, "value"); // 追踪读取操作
                              return value;
                    },
          };
          return obj;
}

// 遍历 obj 对象及其所有嵌套属性
function traverse(value, seen = new Set()) {
          if (typeof value !== "object" || value === null || seen.has(value))
                    return;
          seen.add(value); //标记它已被遍历
          for (const k in value) {
                    traverse(value[k], seen);
          }
          return value;
}
// watch 函数接收 3个参数，source 是响应式数据，cb是回调函数,options配置
function watch(source, cb, options = {}) {
          let getter;
          if (typeof source === "function") getter = source;
          else getter = () => traverse(source);
          let oldValue, newValue;

          let cleanup; //误区：这里的 oninvalidata不会watch直接调用，是开发者在watch使用过程去调用
          const oninvalidate = (fn) => {
                    cleanup = fn;
          };
          const job = () => {
                    newValue = effectFn();
                    if (cleanup) cleanup(); //在回调函数cd之前，先调用注册过期回调
                    cb(newValue, oldValue, oninvalidate); //将 onInvalidate 作为回调函数的第三个参数，以便用户使用
                    oldValue = newValue;
          };
          const effectFn = effect(() => getter(), {
                    lazy: true,
                    scheduler: job,
          });
          if (options.immediate) job();
          else oldValue = effectFn();
}

// 1、定义个 Map 实例，存储原始对象到代理对象的映射
const reactiveMap = new Map();

// reactive
function reactive(obj) {
          // 优先通过原始对象 obj 寻找之前创建的代理对象，如果找到了，直接返回已有的代理对象
          const existionProxy = reactiveMap.get(obj);
          if (existionProxy) return existionProxy;

          // 否则，创建新的代理对象
          const proxy = createReactive(obj);
          // 存储到 Map 中，从而帮忙重复创建
          reactiveMap.set(obj, proxy);
          return proxy;
}
// 浅响应
function showReactive(obj) {
          return createReactive(obj, true);
}
// 深只读
function readonly(obj) {
          return createReactive(obj, false, true); //原数据，是否浅响应，是否只读
}
// 浅只读
function showReadonly(obj) {
          return createReactive(obj, true /*show*/, true);
}

const arrayInstrumentations = {};
["includes", "indexOf", "lastIndexOf"].forEach((method) => {
          const originMethod = Array.prototype[method];
          arrayInstrumentations[method] = function (...args) {
                    // this 是代理对象，现在代理对象中查找，将结果存储到 res 中
                    let res = originMethod.apply(this, args);

                    // res 为 false 说明没有找到，res 是 false 说明没有找到，
                    // 通过 this.raw 拿到原始数组，再去其中查找并更新 res 值
                    if (res === false) {
                              res = originMethod.apply(this.raw, args);
                    }
                    // 返回最终结果
                    return res;
          };
});
// 一个标记变量，代表是否进行追踪，默认值是 true，允许追踪
let shouldTrak = true;
// 重写数组的 push 方法
["push", "shift", "unshift"].forEach((method) => {
          // 取得原始 push 方法
          const originMethod = Array.prototype[method];
          // 重写
          arrayInstrumentations[method] = function (...args) {
                    // 在调用原始方法之前，禁止追踪
                    shouldTrak = false;
                    // push 方法的默认行为
                    let res = originMethod.apply(this, args);
                    // 在调用原始方法之后，恢复原来的行为，即允许追踪
                    shouldTrak = true;
                    return res;
          };
});

// 1、定义一个对象，将自定义的 add 方法定义到该对象上
const mutableInstrumentations = {
          add(key) {
                    const target = this.raw;
                    const hadKey = target.has(key);
                    const res = target.add(key);
                    // 只有在值不存在的情况下，才需要触发响应
                    if (!hadKey) {
                              trigger(target, key, "ADD");
                    }
                    return res;
          },
          delete(key) {
                    const target = this.raw;
                    const hadKey = target.has(key);
                    const res = target.delete(key);
                    // 当要删除的元素确实存在时，才触发响应
                    if (hadKey) {
                              trigger(target, key, "DELETE");
                    }
                    return res;
          },
          get(key) {
                    const target = this.raw;
                    const had = target.has(key);
                    track(target, key);
                    if (had) {
                              const res = target.get(key);
                              return typeof res === "object"
                                        ? reactive(res)
                                        : res;
                    }
          },
          set(key, value) {
                    const target = this.raw;
                    const had = target.has(key);
                    const oldValue = target.get(key);
                    //获取原始数据，由于 value 本身可能已经是原始数据，所以此时 value.raw 不存在，则直接使用 value
                    const rawValue = value.raw || value;
                    target.set(key, rawValue);

                    if (!had) {
                              trigger(target, key, "ADD");
                    } else if (
                              oldValue !== value ||
                              (oldValue === oldValue && value === value)
                    ) {
                              trigger(target, key, "SET");
                    }
          },
};
//封装 createReactive 函数，接收一个参数 isshallow，代表是否为浅响应，默认为 false，即非浅响应
// isReadonly 只读
function createReactive(obj, isShallow = false, isReadonly = false) {
          return new Proxy(obj, {
                    get(target, key, receiver) {
                              // console.log(`get操作,${key}`);

                              // 如果操作的目标是数组，并且 key 存在于 arrayInstrumentations上，
                              // 那么返回定义 在 arrayInstrumentations 上的值
                              if (
                                        Array.isArray(target) &&
                                        arrayInstrumentations.hasOwnProperty(
                                                  key
                                        )
                              ) {
                                        return Reflect.get(
                                                  arrayInstrumentations,
                                                  key,
                                                  receiver
                                        );
                              }
                              if (!isReadonly && typeof key !== "symbol") {
                                        //添加判断，如果 key的类型是 symbol ，则不进行追踪
                                        //性能问题2。.这样 **，性能问题**Symbol：如果 类型副作用的函数值无
                                        // 就不会条件无追意踪间引发副  作用Symbol保持了会代码导致不的必要的稳定副性作用触。
                                        // 发挥影响性能，因为 Symbol 通常不需要被追踪。
                                        track(target, key);
                              }

                              // 代理对象可以通过raw 属性访问原始数据
                              if (key === "raw") {
                                        return target;
                              }
                              if (key === "size") {
                                        //如果读取的是 size 属性
                                        // 通过receiver为原始的的对象来修复问题
                                        track(target, ITERATE_KEY);
                                        return Reflect.get(target, key, target);
                              }
                              // 如果 key 是 add 或 delete，返回 mutableInstrumentations 中的方法
                              if (mutableInstrumentations.hasOwnProperty(key)) {
                                        return mutableInstrumentations[key];
                              }

                              // 得到原始值
                              let res = Reflect.get(target, key, receiver);

                              // 如果是浅响应，则直接返回原始值
                              if (isShallow) {
                                        return res;
                              }

                              if (typeof res === "object" && res !== null) {
                                        //如果数据为只读，则调用readonly 对值进行包装
                                        // 如果值可以被代理，就返回代理对象
                                        return isReadonly
                                                  ? readonly(res)
                                                  : reactive(res);
                              }

                              // 返回 res
                              return res;
                    },
                    set(target, key, newVal, receiver) {
                              if (isReadonly) {
                                        console.warn(`属性 ${key} 是只读的`);
                                        return true;
                              }
                              // 先收集旧的值
                              const oldVal = target[key];
                              let type; // 操作类型
                              if (Array.isArray(target)) {
                                        // 判断数组代理：当前索引比长度 小 属于set，否则为ADD
                                        type =
                                                  Number(key) < target.length
                                                            ? "SET"
                                                            : "ADD";
                              } else {
                                        // 判断对象有某个属性(非原型链继承)，返回布尔值，仅添加时触发副作用
                                        type =
                                                  Object.prototype.hasOwnProperty.call(
                                                            target,
                                                            key
                                                  )
                                                            ? "SET"
                                                            : "ADD";
                              }

                              const res = Reflect.set(
                                        target,
                                        key,
                                        newVal,
                                        receiver
                              );
                              //target===receiver.raw说明receiver就是target的代理对象
                              if (target === receiver.raw) {
                                        // 比较新值与旧值，只要当不全等的时候才触发响应
                                        if (
                                                  oldVal !== newVal &&
                                                  (oldVal === oldVal ||
                                                            newVal === newVal)
                                        ) {
                                                  console.log(`set操作`);
                                                  // 增加第四个参数，响应的新增
                                                  trigger(
                                                            target,
                                                            key,
                                                            type,
                                                            newVal
                                                  );
                                        }
                              }
                              return res;
                    },
                    // has控制属性存在性 in
                    has(target, key) {
                              console.log(`in操作`);
                              track(target, key);
                              return Reflect.has(target, key);
                    },
                    // Object的拦截for...in、Object.key\ ownKeys控制属性的枚举顺序与内容
                    ownKeys(target, key) {
                              console.log(
                                        "%c对象迭代",
                                        "color: purple; font-size: 30px"
                              );
                              track(
                                        target,
                                        Array.isArray(target)
                                                  ? "length"
                                                  : ITERATE_KEY
                              );
                              return Reflect.ownKeys(target);
                    },
                    // 成功删除对象自己的属性时触发更新，删除不存在属性时返回 false
                    deleteProperty(target, key) {
                              if (isReadonly) {
                                        console.warn(`属性 ${key} 是只读的`);
                                        return true;
                              }
                              const hadKey =
                                        Object.prototype.hasOwnProperty.call(
                                                  target,
                                                  key
                                        );
                              const res = Reflect.deleteProperty(target, key);
                              if (res && hadKey) {
                                        trigger(target, key, "DELETE");
                              }
                              return res;
                    },
          });
}

// 仅对 value 进行浅层响应式处理
function ref(val) {
          const wrapper = { value: val };
          Object.defineProperty(wrapper, "__v_isRef", { value: true });
          return reactive(wrapper);
}
// 不对嵌套对象进行响应式转换
function shallowRef(val) {
          const wrapper = { value: val };
          Object.defineProperty(wrapper, "__v_isRef", { value: true });
          return showReactive(wrapper); // 使用 shallowReactive 替代 reactive
}

function toRef(obj, key) {
          const wrapper = {
                    get value() {
                              return obj[key];
                    },
                    set value(val) {
                              obj[key] = val;
                    },
          };
          Object.defineProperty(wrapper, "__v_isRef", { value: true });
          return wrapper;
}
function toRefs(obj) {
          const ret = {};
          for (const key in obj) {
                    //  逐个调用 toRef 完成转换
                    ret[key] = toRef(obj, key);
          }
          return ret;
}
function proxyRefs(target) {
          return new Proxy(target, {
                    get(target, key, receiver) {
                              const value = Reflect.get(target, key, receiver);
                              return value.__v_isRef ? value.value : value;
                    },
                    set(target, key, newValue, receiver) {
                              // 通过 target 读取真实性
                              const value = target[key];
                              // 如果值是 Ref ,则设置其对应的vlaue 属性值
                              if (value.__v_isRef) {
                                        value.value = newValue;
                                        return true;
                              }
                              return Reflect.set(
                                        target,
                                        key,
                                        newValue,
                                        receiver
                              );
                    },
          });
}

// const obj = reactive({ foo: 1, bar: 2 });
// const newObj = proxyRefs({ ...toRefs(obj) });

// effect(() => {
//           console.log(newObj.foo); //1
//           console.log(newObj.bar); //2
// });
// newObj.foo = 23;
export {
          effect,
          reactive,
          ref,
          toRef,
          toRefs,
          proxyRefs,
          computed,
          watch,
          showReactive,
          showReadonly,
          shallowRef,
};
